#include "Calendar.h"
#include <math.h>

/* 辅助宏定义,提取整数部分 */
#define INT(value) ((int32)((value) >= 0 ? (value) : ((value)-1)))

/* 公历转儒略日 */
JulianDay julianDayForDate(float day, int month, int year)
{
    JulianDay julianDay;
    
    if (month <= 2)
    {
        year = year - 1;
        month = month + 12;
    }
    
    int32 A = INT(year/100.0);
    int32 B = 2 - A + INT(A/4);
    
    julianDay.day  = INT(365.25*(year+4716)) + INT(30.6001*(month+1)) + (int)(day) + B - 1524.5;
    julianDay.time = 0.5 + (day - (int)day);
    
    /* 调整日期和时间 */
    if (julianDay.time >= 1)
    {
        julianDay.time -= 1;
        julianDay.day += 1;
    }
    
    return julianDay;
}

JulianDay julianDayForDateAndTime(int day, int month, int year,
                                 int hours, int minutes, int seconds)
{
    return julianDayForDate(day + hours/24.0 + minutes/1440.0 + seconds/86400.0, month, year);
}

/* 儒略日转公历 */
void dateForJulianDay(JulianDay julianDay, float *day, int *month, int *year)
{
    int32 Z = julianDay.day;
    float F = julianDay.time + 0.5;
    int32 A;
    
    /* 调整日期和时间 */
    if (F >= 1)
    {
        F -= 1;
        Z += 1;
    }
    
    
    if (Z < 2299161)
    {
        A = Z;
    }
    else
    {
        int32 alpha = INT((Z-1867216.25)/36524.25);
        A = Z + 1 + alpha - INT(alpha/4);
    }
    
    int32 B = A + 1524;
    int32 C = INT((B-122.1)/365.25);
    int32 D = INT(365.25*C);
    int32 E = INT((B-D)/30.6001);
    
    *day   = B - D - INT(30.6001*E) + F;
    if (E < 14)
    {
        *month = (unsigned)(E - 1);
    }
    else
    {
        *month = (unsigned)(E - 13);
    }
    
    if (*month > 2)
    {
        *year = (unsigned)(C - 4716);
    }
    else
    {
        *year = (unsigned)(C - 4715);
    }
}

void dateAndTimeForJulianDay(JulianDay julianDay, int *day, int *month, int *year,
                            int *hours, int *minutes, int *seconds)
{
    /* 计算日期（日为浮点数） */
    float floatingDay;
    dateForJulianDay(julianDay, &floatingDay, month, year);
    
    /* 存储整数日 */
    *day = (int)floatingDay;
    
    /* 保留小数部分 */
    floatingDay -= *day;
    
    /* 计算时、分、秒 */
    *hours   = floatingDay * 24;
    *minutes = floatingDay * 1440 - *hours * 60;
    *seconds = floatingDay * 86400 - *hours * 3600 - *minutes * 60;
}

/* 星期计算 */
unsigned int weekDayForDate(int day, int month, int year)
{
    return weekDayForJulianDay(julianDayForDate(day, month, year));
}

unsigned int weekDayForJulianDay(JulianDay julianDay)
{
    return (unsigned int)(julianDay.day + julianDay.time + 1.5) % 7;
}
